package com.aps.service.impl;

import java.util.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import com.aps.entity.domain.device.DeviceCenter;
import com.aps.entity.domain.dish.Dish;
import com.aps.entity.domain.dish.DishInterval;
import com.aps.entity.domain.order.ApsOrder;
import com.aps.entity.domain.order.OrderDish;
import com.aps.entity.dto.*;
import com.aps.entity.eunm.*;
import com.aps.service.*;
import com.aps.service.rep.*;
import com.aps.util.Constant;
import com.aps.util.DateUtil;
import com.aps.util.JdbcQueryUtil;
import com.aps.util.ScheduleComponent;

import lombok.extern.slf4j.Slf4j;

/**
 * @author liaoguangan
 * @description <订单菜品服务实现类>
 * @date 2018/4/1 15:44
 */

@Slf4j
@Service
public class OrderDishServiceImpl implements IOrderDishService {

    @Autowired
    private OrderDishRep orderDishRep;

    @Autowired
    private DeviceCenterRep deviceCenterRep;

    @Autowired
    private IUnChargeService unChargeService;

    @Autowired
    private IApsOrderService orderService;

    @Autowired
    private IKdsService kdsService;

    @Autowired
    private ApsOrderRep apsOrderRep;

    @Autowired
    private ScheduleComponent scheduleComponent;

    @Autowired
    private DishIntervalReq dishIntervalReq;

    @Autowired
    private IDishTypeSeqService dishTypeSeqService;

    @Autowired
    private IDishProcessService dishProcessService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private DishRep dishRep;

    /**
     * 菜品开始制作
     *
     * @param kdsDishOpeDTO
     */
    @Override
    public void start(KdsDishOpeDTO kdsDishOpeDTO) {
        //菜品开始制作
        OrderDish orderDish = getByKdsDishOpeDTO(kdsDishOpeDTO);

        Dish dish = dishRep.findByCode(orderDish.getDishNum());
        dishProcessService.updateDishProcessTime(dish, orderDish.getDspReq(),
                orderDish.getDishNum());

        orderDish.setStatus(DishStatus.START);
        orderDish.setStartTime(kdsDishOpeDTO.getActualStime());

        //设置菜品具体加工设备模块ID和完成时间
        orderDish.setDeviceModuleCode(kdsDishOpeDTO.getCtrSlotid());
        orderDish.setCompleteTime(
                DateUtil.addTime(dish.getProcessTimeUnit(), dish.getProcessTime().intValue()));

        orderDishRep.save(orderDish);

        //更新设备状态
        DeviceCenter deviceCenter = deviceCenterRep.findByCode(kdsDishOpeDTO.getCtrSlotid());
        deviceCenter.setStatus(AdjustType.USED);
        deviceCenterRep.save(deviceCenter);

        //更新订单状态
        orderService.setOrderStauts(kdsDishOpeDTO.getOrderNum());
    }

    /**
     * 菜品结束制作
     *
     * @param kdsDishOpeDTO
     */
    @Override
    public void end(KdsDishOpeDTO kdsDishOpeDTO) {
        //菜品开始制作
        OrderDish orderDish = getByKdsDishOpeDTO(kdsDishOpeDTO);

        orderDish.setStatus(DishStatus.COMPLETE);
        orderDish.setStartTime(kdsDishOpeDTO.getActualComtime());
        orderDishRep.save(orderDish);

        //更新设备状态
        DeviceCenter deviceCenter = deviceCenterRep.findByCode(kdsDishOpeDTO.getCtrSlotid());
        deviceCenter.setStatus(AdjustType.RELEASE);
        deviceCenterRep.save(deviceCenter);

        //查看是否为退菜菜品
        if (orderDish.getStatus().equals(DishStatus.BACK)) {
            //存放到待分配队列
            unChargeService.add(orderDish);
        }

        //更新订单状态
        Integer count = orderDishRep.countByOrderNumAndStatusNotAndStatusNot(
                kdsDishOpeDTO.getOrderNum(), OrderStatus.COMPLETE, OrderStatus.BACK);
        if (count == 0) {
            ApsOrder order = apsOrderRep.findByOrderNum(kdsDishOpeDTO.getOrderNum());
            order.setStatus(OrderStatus.COMPLETE);
            apsOrderRep.save(order);

            //移除待排程缓存队列
            scheduleComponent.removeSchedule(kdsDishOpeDTO.getOrderNum());
        }
    }

    /**
     * 加菜
     * @param orderNum
     * @param orderDishChangeDTO
     * @param status
     */
    @Override
    public void add(String orderNum, OrderDishChangeDTO orderDishChangeDTO, DishStatus status) {
        OrderDish orderDish = new OrderDish();
        orderDish.setOrderNum(orderNum);
        orderDish.setStatus(status);
        orderDish.setComment(orderDishChangeDTO.getComment());
        orderDish.setDishNum(orderDishChangeDTO.getDishNum());
        orderDish.setDishSeq(orderDishChangeDTO.getDishSeq());
        orderDish.setDspReq(orderDishChangeDTO.getDspReq());
        orderDish.setHoldSign(false);
        orderDish.setQuty(orderDishChangeDTO.getQuty());

        orderDishRep.save(orderDish);

        //变更订单状态
        ApsOrder order = apsOrderRep.findByOrderNum(orderNum);
        order.setDishStatus(status);
        if (order.getStatus().equals(OrderStatus.COMPLETE)) {
            order.setStatus(OrderStatus.UNSTART);
        }
        apsOrderRep.save(order);

        //添加待排程缓存队列
        scheduleComponent.addSchedule(orderNum);
    }

    /**
     * 退菜
     *
     * @param orderDish
     */
    @Override
    public void back(OrderDish orderDish) {
        //菜品是否开始
        if (orderDish.getStatus().equals(DishStatus.COMPLETE)) {
            //存放到待分配队列
            unChargeService.add(orderDish);
        }
        orderDish.setStatus(DishStatus.BACK);
        orderDishRep.save(orderDish);

        //订单变更反馈KDS
        kdsService.change(new OrderChangeToKdsDTO(orderDish.getOrderNum(), orderDish.getDishSeq(),
                orderDish.getDishNum(), OrderChangeType.BACK.getCode()));
    }

    /**
     * 计算RT时间
     *
     * @param apsOrders
     */
    @Override
    public List<ApsOrderRtDTO> calculateRequestTime(List<ApsOrder> apsOrders) {
        List<ApsOrderRtDTO> apsOrderRtDTOS = new ArrayList<>();
        for (ApsOrder order : apsOrders) {
            List<ApsOrderDishDbDTO> list = orderDishRep.findDishByDishNum(order.getOrderNum());

            //计算DueTime
            this.dueTime(order, list);

            //计算RequestTime
            this.requestTime(order, list);

            apsOrderRtDTOS.add(new ApsOrderRtDTO(order, list));
        }
        return apsOrderRtDTOS;
    }

    private void dueTime(ApsOrder order, List<ApsOrderDishDbDTO> list) {
        Dish dish = null;
        OrderDish orderDish = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            dish = apsOrderDishDbDTO.getDish();
            orderDish = apsOrderDishDbDTO.getOrderDish();
            dishProcessService.updateDishProcessTime(dish, orderDish.getDspReq(),
                    orderDish.getDishNum());
            apsOrderDishDbDTO.setDueTime(DateUtil.addTime(order.getOrderTime(),
                    dish.getProcessTimeUnit(), dish.getProcessTime().intValue())); //下单时间加加工时长

            //初始RT
            apsOrderDishDbDTO.setInitRequestTime(apsOrderDishDbDTO.getDueTime());
        }
    }

    private void requestTime(ApsOrder order, List<ApsOrderDishDbDTO> list) {
        //上菜顺序
        OrderDishType type = order.getOspReq();
        if (!order.getOrderType().equals(OrderType.T02)) { //非外卖
            switch (type) {
                case OR1: { //正常顺序上菜
                    this.or1(list);
                    break;
                }
                case OR2: { //先出先上
                    this.or2(order, list);
                    break;
                }
                case OR3: { //主菜一起上
                    this.or3(list);
                    break;
                }
                default: {

                }
            }
        } else { //外买时一起上菜
            this.takeOut(order, list);
        }

        //记住上一个菜品的RequestTme
        for (int k = 0; k < list.size(); k++) {
            if (k > 0) {
                list.get(k).setPreRequestTime(list.get(k - 1).getRequestTime());
            }
        }
    }

    /**
     * 倒序重新计算RequestTime
     * @param list
     */
    public void reverseCalRt(List<ApsOrderDishDbDTO> list) {
        //计算最终RT
        int size = list.size() - 1;
        for (int j = size; j >= 0; j--) {
            if (j < size) {
                ApsOrderDishDbDTO apsOrderDishDbDTO = list.get(j);
                ApsOrderDishDbDTO tempDto = list.get(j + 1);
                //获取菜品类别前序间隔时长
                DishInterval interval = dishIntervalReq.findByPreDishCodeAndNextDishCode(
                        apsOrderDishDbDTO.getDish().getDishTypeCode(),
                        tempDto.getDish().getDishTypeCode());

                if (null == interval) {
                    apsOrderDishDbDTO.setRequestTime(tempDto.getRequestTime());
                } else {
                    apsOrderDishDbDTO.setRequestTime(DateUtil.addTime(tempDto.getRequestTime(),
                            interval.getConstraintTimeUnit(),
                            -interval.getConstraintTime().intValue()));
                }
            }
        }
    }

    private void or1(List<ApsOrderDishDbDTO> list) {
        //按配置的上菜顺序排序
        dishTypeSeqService.sort(list);
        int i = 0;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            if (i == 0) { //第一个菜品前序间隔时长为0
                apsOrderDishDbDTO.setPreAndIntervalTime(apsOrderDishDbDTO.getInitRequestTime());
                apsOrderDishDbDTO.setRequestTime(apsOrderDishDbDTO.getInitRequestTime());
            } else {
                ApsOrderDishDbDTO tempDto = list.get(i - 1);

                //获取菜品类别前序间隔时长
                DishInterval interval = dishIntervalReq.findByPreDishCodeAndNextDishCode(
                        tempDto.getDish().getDishTypeCode(),
                        apsOrderDishDbDTO.getDish().getDishTypeCode());

                //上一个菜品RT+间隔时长
                if (null == interval) { //未设置
                    apsOrderDishDbDTO.setPreAndIntervalTime(tempDto.getRequestTime());
                } else {
                    apsOrderDishDbDTO.setPreAndIntervalTime(DateUtil.addTime(
                            tempDto.getRequestTime(), interval.getConstraintTimeUnit(),
                            interval.getConstraintTime().intValue()));
                }

                //设置RT
                apsOrderDishDbDTO.setRequestTime(apsOrderDishDbDTO.getPreAndIntervalTime());
                if (apsOrderDishDbDTO.getInitRequestTime()
                        .after(apsOrderDishDbDTO.getPreAndIntervalTime())) {
                    apsOrderDishDbDTO.setRequestTime(apsOrderDishDbDTO.getInitRequestTime());
                }
            }
            i++; //记录第几个菜品
        }
        this.reverseCalRt(list);
    }

    private void or2(ApsOrder order, List<ApsOrderDishDbDTO> list) {
        ApsOrderDishDbDTO first = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            apsOrderDishDbDTO.setDueTime(DateUtil.addTime(order.getOrderTime(),
                    apsOrderDishDbDTO.getDish().getProcessTimeUnit(),
                    apsOrderDishDbDTO.getDish().getProcessTime().intValue())); //下单时间加加工时长

            apsOrderDishDbDTO.setRequestTime(apsOrderDishDbDTO.getDueTime());

            if (apsOrderDishDbDTO.getOrderDish().getIsFirst()) {
                first = apsOrderDishDbDTO; //先上菜品
            }
        }

        if (null != first) {
            //设置最终RT
            for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
                if (first.getRequestTime().after(apsOrderDishDbDTO.getRequestTime())) {
                    apsOrderDishDbDTO.setRequestTime(first.getRequestTime());
                }
            }
        }
    }

    private void or3(List<ApsOrderDishDbDTO> list) {
        this.or1(list); //按照正常上菜顺序计算所有菜品Request Time

        //计算出主菜 Max(RT)
        Date mainMaxDate = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            if (apsOrderDishDbDTO.getDish().getDishTypeCode().equals(Constant.MAIN_FOOD)) { //是主菜
                if (null == mainMaxDate) {
                    mainMaxDate = apsOrderDishDbDTO.getRequestTime();
                } else {
                    if (apsOrderDishDbDTO.getRequestTime().after(mainMaxDate)) {
                        mainMaxDate = apsOrderDishDbDTO.getRequestTime();
                    }
                }
            }
        }

        if (null == mainMaxDate) { //没有主菜
            return;
        }
        //重算菜品RT
        List<ApsOrderDishDbDTO> tempList = new ArrayList<>();
        //缓存一份主菜用于计算其他菜品RT
        ApsOrderDishDbDTO main = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            if (apsOrderDishDbDTO.getDish().getDishTypeCode().equals(Constant.MAIN_FOOD)) { //是主菜
                apsOrderDishDbDTO.setRequestTime(mainMaxDate);
                main = apsOrderDishDbDTO;
            } else if (!apsOrderDishDbDTO.getDish().getDishTypeCode().equals(Constant.MAIN_FOOD)
                    && (apsOrderDishDbDTO.getRequestTime().before(mainMaxDate)
                            || apsOrderDishDbDTO.getRequestTime().equals(mainMaxDate))) { //在主菜之前的菜品
                tempList.add(apsOrderDishDbDTO);
            }
        }
        tempList.add(main);

        this.reverseCalRt(tempList);

    }

    private void takeOut(ApsOrder order, List<ApsOrderDishDbDTO> list) {
        //计算默认RT
        this.dueTime(order, list);
        Date maxRt = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            if (null == maxRt) {
                maxRt = apsOrderDishDbDTO.getInitRequestTime();
            } else {
                if (apsOrderDishDbDTO.getInitRequestTime().after(maxRt)) {
                    maxRt = apsOrderDishDbDTO.getInitRequestTime();
                }
            }
        }

        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            apsOrderDishDbDTO.setRequestTime(maxRt);
        }
    }

    private OrderDish getByKdsDishOpeDTO(KdsDishOpeDTO kdsDishOpeDTO) {
        return orderDishRep.findByOrderNumAndDishNumAndDishSeq(kdsDishOpeDTO.getOrderNum(),
                kdsDishOpeDTO.getDishNum(), kdsDishOpeDTO.getDishSeq());
    }

    /**
     * 计算PT时间
     *
     * @param apsOrders
     */
    @Override
    public void calculatePromiseTime(List<ApsOrderRtDTO> apsOrders) {
        //设置初始startTime
        ApsOrder order = null;
        List<ApsOrderDishDbDTO> apsOrderDishDbDTOS = null;

        //加载设备信息并分组
        Map<String, List<DeviceModuleDTO>> dmGroup = this.dmGroup(apsOrders);

        for (ApsOrderRtDTO apsOrderRtDTO : apsOrders) {
            apsOrderDishDbDTOS = apsOrderRtDTO.getList();
            order = apsOrderRtDTO.getApsOrder();
            //计算默认开始时间
            this.startTime(apsOrderDishDbDTOS);

            //按配置的上菜顺序排序
            //dishTypeSeqService.sort(apsOrderDishDbDTOS);

            //计算PromiseTime
            this.promiseTime(order, apsOrderDishDbDTOS, dmGroup);
        }

        //按加工设备类型分组
        // Map<String, List<ApsOrderDishDbDTO>> group = this.dishGroup(apsOrders);

        //this.device(group);

    }

    private void promiseTime(ApsOrder order, List<ApsOrderDishDbDTO> list,
            Map<String, List<DeviceModuleDTO>> dmGroup) {
        //上菜顺序
        OrderDishType type = order.getOspReq();
        if (!order.getOrderType().equals(OrderType.T02)) { //非外卖
            switch (type) {
                case OR1: { //正常顺序上菜
                    this.por1(order, list, dmGroup);
                    break;
                }
                case OR2: { //先出先上
                    break;
                }
                case OR3: { //主菜一起上
                    break;
                }
                default: {

                }
            }
        } else { //外买时一起上菜
            this.takeOut(order, list);
        }
    }

    private void startTime(List<ApsOrderDishDbDTO> list) {
        Dish dish = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            dish = apsOrderDishDbDTO.getDish();
            apsOrderDishDbDTO.setStartTime(DateUtil.addTime(apsOrderDishDbDTO.getRequestTime(),
                    dish.getProcessTimeUnit(), -dish.getProcessTime().intValue()));
        }
    }

    private Map<String, List<DeviceModuleDTO>> dmGroup(List<ApsOrderRtDTO> apsOrders) {
        List<String> codeList = new ArrayList<>();
        for (ApsOrderRtDTO dto : apsOrders) {
            for (ApsOrderDishDbDTO apsOrderDishDbDTO : dto.getList()) {
                String key = apsOrderDishDbDTO.getDish().getDeviceTypeCode();
                if (!codeList.contains(key)) {
                    codeList.add(key);
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        for (String k : codeList) {
            sb.append("'" + k + "'");
            sb.append(",");
        }
        sb.deleteCharAt(sb.lastIndexOf(","));
        String codes = sb.toString();

        //查询出所有关联的设备模块
        String sql = "SELECT adm.code, adm.name, adm.device_spec_code, adm.device_code, "
                + " ads.code as spec_code, ads.length, ads.width, ads.volume, "
                + " adc.device_type_code" + " from aps_device_module adm "
                + " left join aps_device_spec ads on adm.device_spec_code = ads.code "
                + " left join aps_device_center adc on adm.device_code = adc.code "
                + " where adc.device_type_code in (" + codes + ")";
        List<DeviceModuleJpaDTO> dmsJpa = new JdbcQueryUtil<>(DeviceModuleJpaDTO.class)
                .queryList(jdbcTemplate, sql, new Object[] {});

        // List<DeviceModuleJpaDTO> dmsJpa = deviceCenterRep.findAllModuleByCodes(codes);
        List<DeviceModuleDTO> dms = new ArrayList<>();
        for (DeviceModuleJpaDTO dmjd : dmsJpa) {
            dms.add(dmjd.build());
        }

        //计算所有设备最小可用时间
        for (DeviceModuleDTO dmd : dms) {
            dmd.setEnableTime(this.enableTime(dmd.getDeviceModule().getCode()));
        }

        //针对设备模块进行分组
        Map<String, List<DeviceModuleDTO>> dmGroup = new HashMap<>();
        for (DeviceModuleDTO dm : dms) {
            String key = dm.getDeviceTypeCode();
            if (dmGroup.get(key) == null) {
                dmGroup.put(key, new ArrayList<>());
            }
            dmGroup.get(key).add(dm);
        }
        return dmGroup;
    }

    private void por1(ApsOrder order, List<ApsOrderDishDbDTO> list,
            Map<String, List<DeviceModuleDTO>> group) {

        //保存结果
        Map<String, List<ApsOrderDishDbDTO>> gtt = new HashMap<>();

        ApsOrderDishDbDTO preTempApsDishDTO = null;
        for (ApsOrderDishDbDTO apsOrderDishDbDTO : list) {
            String deviceTypeCode = apsOrderDishDbDTO.getDish().getDeviceTypeCode();
            for (Map.Entry entry : group.entrySet()) {
                if (entry.getKey().toString().equals(deviceTypeCode)) {
                    List<DeviceModuleDTO> tempDms = (List<DeviceModuleDTO>) entry.getValue();
                    //按可用时间排序
                    this.sortByTime(tempDms);
                    if (this.hasSpec(tempDms)) {

                    } else { //设备模块没有设置规格时，默认单个模块只能同时加工单个菜品
                        if (null != tempDms && tempDms.size() > 0) {
                            DeviceModuleDTO dmd = tempDms.get(0);
                            if (null != apsOrderDishDbDTO.getIsFirst()
                                    && apsOrderDishDbDTO.getIsFirst()) { //第一个菜品
                                apsOrderDishDbDTO.setPromiseTime(DateUtil.addTime(
                                        this.maxDate(dmd.getEnableTime(),
                                                apsOrderDishDbDTO.getStartTime()),
                                        apsOrderDishDbDTO.getDish().getProcessTimeUnit(),
                                        apsOrderDishDbDTO.getDish().getProcessTime().intValue()));
                                preTempApsDishDTO = apsOrderDishDbDTO;
                            } else {
                                int tempPrt = (int) ((preTempApsDishDTO.getPromiseTime().getTime()
                                        - preTempApsDishDTO.getRequestTime().getTime()) / 1000
                                        / 60);
                                Date tempRt = DateUtil.addTime(apsOrderDishDbDTO.getRequestTime(),
                                        TimeUnitType.M, tempPrt);

                                //可用时间加加工时间 和 预计的PromiseTime作比较  取大
                                if (dmd.getEnableTime() == null) {
                                    apsOrderDishDbDTO.setPromiseTime(tempRt);
                                } else {
                                    apsOrderDishDbDTO
                                            .setPromiseTime(this.maxDate(
                                                    DateUtil.addTime(dmd.getEnableTime(),
                                                            apsOrderDishDbDTO.getDish()
                                                                    .getProcessTimeUnit(),
                                                            apsOrderDishDbDTO.getDish()
                                                                    .getProcessTime().intValue()),
                                                    tempRt));
                                }
                            }
                            dmd.setEnableTime(apsOrderDishDbDTO.getPromiseTime());
                        }
                    }
                }
            }
        }

    }

    private Date maxDate(Date d1, Date d2) {
        if (d1 == null) {
            return d2;
        }
        if (d1.after(d2)) {
            return d1;
        }
        return d2;
    }

    private Boolean hasSpec(List<DeviceModuleDTO> dms) {
        for (DeviceModuleDTO dm : dms) {
            if (dm.getDeviceSpec() == null || dm.getDeviceSpec().getCode() == null) {
                return false;
            }
        }
        return true;
    }

    private Date enableTime(String deviceModuleCode) {
        return orderDishRep.enableTime(deviceModuleCode);
    }

    private void sortByTime(List<DeviceModuleDTO> dms) {

        Collections.sort(dms, new Comparator<DeviceModuleDTO>() {

            @Override
            public int compare(DeviceModuleDTO o1, DeviceModuleDTO o2) {
                return o1.getEnableTime().after(o2.getEnableTime()) ? 1 : -1;
            }
        });
    }

    private void por2() {
    }

    private void por3() {
    }
}
